//
//  BTManager.m
//  BTDemo
//
//  Created by Avery on 14-8-22.
//  Copyright (c) 2014年 Alvin. All rights reserved.
//

#import "BTManager.h"
#import "Device.h"
#import "BTSendData.h"

static  int const  RECV_BUFFER_SIZE       = 12;

@interface BTManager()<EAAccessoryDelegate,NSStreamDelegate>
{
    @private
    /* Accessory Related */
    EAAccessoryManager *accessoryManager;     //External Accessory Manager
    EAAccessory        *accessory;            //Accessory Object
    EASession          *session;              //Communication Session
    
    NSInputStream      *input;                //Communication Input stream
    NSOutputStream     *output;               //Communication Output stream
    
    BOOL               isConnected;           //Current connect state
    
    /* Command Related */
    LinkedArray        *sendQue;
    BTSendData            *currentSendData;
    
    /* Timeout Related */
    NSTimer   *responseTimer;                //Response Timer for command

}

@end

@implementation BTManager

#pragma mark Life cyc
SingleInstance(BTManager)

- (id)init
{
    self = [super init];
    if(self)
    {
        accessoryManager = [EAAccessoryManager sharedAccessoryManager];
        sendQue          = [[LinkedArray alloc] init];
    }
    return self;
}

#pragma mark Public Methods

- (void)connectToDevice
{
    //Connected state! current device already connected.
    if (isConnected) {
        return;
    }
    
    //To release the bluetooth objects first, make sure no suitable device connected now!
    [self releaseAction];
    
    //Create connection to the device and create streams on connection!
    NSArray * connectedDevices = [accessoryManager connectedAccessories];
    if([connectedDevices count] > 0)
    {
        MLog(@"connected devices count = %lu", (unsigned long)connectedDevices.count);
        for(EAAccessory *_accessory in connectedDevices)
        {
            for(NSString *protocol in _accessory.protocolStrings)
            {
                if ([protocol isEqualToString:BTProtocol]) {
                    MLog(@"protocol name = %@", protocol);
                    accessory = _accessory;
                    accessory.delegate = self;
                    session = [[EASession alloc] initWithAccessory:accessory forProtocol:protocol];
                }
            }
        }
        if (session) {
            [self setupStream];
            isConnected = YES;
            //When current bluetooth is connected, check the connect state repeatly!
            [self checkConnectState];
            //Call back the current bluetooth state
            self.completion_status(BTStatusConnected);
        }
    } else {
        MLog(@"no External Accessory framework compatible devices connected");
    }
}

- (void)disconnectToDevice
{
    [self releaseAction];
}

- (BOOL)isDeviceConnected
{
    return isConnected;
}

- (void)checkConnectState
{
    BOOL bCatchDevice = NO;
    NSArray * connectedDevices = [accessoryManager connectedAccessories];
    if([connectedDevices count] > 0)
    {
        for(EAAccessory *_accessory in connectedDevices)
        {
            for(NSString *protocol in _accessory.protocolStrings)
            {
                if ([protocol isEqualToString:BTProtocol]) {
                    bCatchDevice = YES;
                    [self performSelector:@selector(checkConnectState) withObject:nil afterDelay:0.003];
                }
            }
        }
    } else {
        bCatchDevice = NO;
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(checkConnectState) object:nil];
    }
    
    if (bCatchDevice) {
        isConnected = YES;
    }else{
        isConnected = NO;
    }
//    NSLog(@"Connect state :%d",isConnected);
}

- (Device *)currentDevice
{
    Device *device = nil;
    if(accessory)
    {
        device = [[Device alloc] init];
        device.deviceName = accessory.name;
        device.uuidString = accessory.serialNumber;
        device.deviceID   = [accessory.serialNumber hash];
    }
    
    return device;
}


- (void)sendData:(BTSendData *)data
{
    if(!input || !output || !data)return;
    currentSendData = data;
    [self startResponseTimer];
    [sendQue addObject:data];
    if([sendQue count] > 0)
    {
        [self runSendQue];
    }
}

- (void)releaseAction
{
    if(accessory)
    {
        accessory.delegate = nil;
        accessory          = nil;
    }
    
    if(input)
    {
        [input close];
        input = nil;
    }
    
    if(output)
    {
        [output close];
        output = nil;
    }
    
    if(session)
    {
        session = nil;
    }
    
    isConnected = NO;
}


#pragma mark Private Methods
- (void)setupStream
{
    if(!session){
        NSLog(@"Session is not exsit or already connected!");
        return ;
    }
    
    input  = session.inputStream;
    output = session.outputStream;
    
    [input scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [output scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    input.delegate  = self;
    output.delegate = self;
    
    [input open];
    [output open];
}

- (void)runSendQue
{
    while ([sendQue count] > 0) {
        BTSendData *sendData = [sendQue objectAtIndex:0];
        [self performSelector:@selector(executeSendData:) withObject:[sendData value] afterDelay:SEND_CMD_DURATION];
        [sendQue removeObjectAtIndex:0];
    }
}

- (void)executeSendData:(NSData *)sendData
{
    NSData *data = sendData;
    uint8_t *readBytes = (uint8_t *)[data bytes];
    [output write:readBytes maxLength:data.length];
    MLog(@"write %@", output);
}

#pragma mark Timer Manage
- (void)startResponseTimer
{
    [self stopResponseTimer];
    responseTimer = [NSTimer scheduledTimerWithTimeInterval:currentSendData.responseTimeout
                                                     target:self
                                                   selector:@selector(responseTimeout)
                                                   userInfo:nil
                                                    repeats:NO];
}

- (void)responseTimeout
{
    responseTimer = nil;
    NSError *error = [NSError errorWithDomain:@"" code:BTSendDataCallBackTimeout userInfo:nil];
    currentSendData.responseCallBack(error,nil);
}

- (void)stopResponseTimer
{
    if(responseTimer)
    {
        [responseTimer invalidate];
        responseTimer = nil;
    }
}

#pragma mark EAAccessory Delegate
- (void)accessoryDidDisconnect:(EAAccessory *)_accessory
{

    if ([accessory isEqual:_accessory]
        && self.completion_status)
    {
        isConnected = NO;
        MLog(@" accessory Did Disconnect ... ");
        self.completion_status(BTStatusDisconnect);
    }
}

#pragma mark NSStream  Delegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventEndEncountered:
            MLog(@"NSStreamEventEndEncountered");
            break;
        case NSStreamEventHasBytesAvailable:
        {
            MLog(@"NSStreamEventHasBytesAvailable");
            NSMutableData *data = [NSMutableData new];
            if([aStream isEqual:input])
            {
                uint8_t buf[RECV_BUFFER_SIZE];
                while ([input hasBytesAvailable]) {
                    NSInteger bytes = [input read:buf maxLength:RECV_BUFFER_SIZE];
                    [data appendBytes:(void *)buf length:bytes];
                }
                MLog(@"Read data = %@ str = %@",data, [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
                
                [self handleReceiveData:data];
            }
        }
            break;
        case NSStreamEventHasSpaceAvailable:
            MLog(@"NSStreamEventHasSpaceAvailable");
            if([aStream isEqual:output])
            {
                MLog(@" output ");
            }
            else
            {
                MLog(@" input ");
            }
            break;
        case NSStreamEventOpenCompleted:
            MLog(@"NSStreamEventOpenCompleted");
            break;
        default:
            break;
    }
}

- (void)handleReceiveData:(NSData *)data
{
    if(self.completion_Log)
    {
        NSString *message = string(@"recv: %@", data);
        self.completion_Log(message);
    }
    
    
    if(currentSendData)
    {
        BTSendDataCallBack callBackCase = currentSendData.verifyCallBack(data);
        switch (callBackCase) {
            case BTSendDataCallBackContinue:
            {
                [currentSendData.responseDatas addObject:data];
            }
                break;
            case BTSendDataCallBackFinish:
            {
                [self stopResponseTimer];
                [currentSendData.responseDatas addObject:data];
                [currentSendData callBackResponseModel];
                // currentSendData = nil;
            }
                break;
            case BTSendDataCallBackIgnore:
            {
                MLog(@"BTSendDataCallBackIgnore");
            }
                break;
            default:
                break;
        }
        
    }
    
    //Response Seek
    
}

- (void)scanDevice
{
    if(is_IOS7){
        [accessoryManager showBluetoothAccessoryPickerWithNameFilter:nil completion:^(NSError *error) {
            if(error != nil && [error code] == EABluetoothAccessoryPickerResultCancelled)
            {
                MLog(@"error");
            }
            
        }];
    }else{
         [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=General&path=Bluetooth"]];
    }
}

@end
